﻿using NAudio.Wave;
using Nes.Core;
using Nes.Core.PPUS;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar;

namespace Nes4Core.UI
{
    public partial class NesNT : Form
    {
        private INes nes;
        private System.Threading.Timer timer;
        private System.Threading.Timer timer2;

        #region
        private WaveOut player;
        private AudioWaveProvider audioWaveProvider;
        #endregion

        public NesNT()
        {
            InitializeComponent();
            this.KeyPreview = true;
            this.KeyDown += Form1_KeyDown;
            this.KeyUp += Form1_KeyUp;
        }

        private void HandleFrame(Byte[] buff)
        {
            var tmpArr = buff.Clone() as Byte[];
            pictureBox1.Image = Int32ToImage_v2(tmpArr);
        }

        private Image Int32ToImage(Byte[] buffer)
        {
            var bm = new Bitmap(256, 240);
            for (var i = 0; i < buffer.Length; i++)
            {
                var rgb = PaletteTable.GetColor(buffer[i]);
                var x = i % 256;
                var y = i / 256;
                bm.SetPixel(x, y, rgb);
            }
            return bm;
        }

        private unsafe Image Int32ToImage_v2(Byte[] buffer)
        {
            var bm = new Bitmap(256, 240);
            fixed (Byte* ptr = buffer)
            {
                var buffLen = buffer.Length;
                var currentPtr = ptr;
                for (var i = 0; i < buffLen; i++)
                {
                    var rgb = PaletteTable.GetColor(*currentPtr);
                    currentPtr++;
                    var x = i % 256;
                    var y = i / 256;
                    bm.SetPixel(x, y, rgb);
                }
            }
            return bm;
        }

        private const Int32 ScreenWith = 256;
        private const Int32 ScreenHeight = 240;

        public unsafe Bitmap Int32ToImage_v3(Byte[] buffer)
        {
            var bm = new Bitmap(ScreenWith, ScreenHeight, PixelFormat.Format24bppRgb);
            Rectangle rect = new Rectangle(0, 0, ScreenWith, ScreenHeight);
            // 锁定区域内的位图信息
            BitmapData bmpData = bm.LockBits(rect, ImageLockMode.ReadWrite, bm.PixelFormat);
            // 步长
            var stride = bmpData.Stride;
            // 获取指向第一行的指针
            IntPtr line0 = bmpData.Scan0;
            var color = new Byte[stride];
            Span<Byte> color_ = new(color);
            fixed (Byte* ptr = buffer)
            {
                var currentPtr = ptr;
                for (var row = 0; row < ScreenHeight; row++)
                {
                    color_.Clear();
                    for (var col = 0; col < ScreenWith; col++)
                    {
                        var rgb = PaletteTable.GetColor2(*currentPtr);
                        currentPtr++;
                        color_[col * 3] =     rgb[3];
                        color_[col * 3 + 1] = rgb[2];
                        color_[col * 3 + 2] = rgb[1];
                    }
                    Marshal.Copy(color, 0, line0, stride);
                    line0 += stride;
                }
            }
            // 解锁位图数据
            bm.UnlockBits(bmpData);
            return bm;
        }

        private void RefreshStatus(Object state)
        {
            Invoke(() =>
            {
                tsslFPS.Text = $"FPS: {nes.GetFps()}";
            });
        }

        private static Int64 lastTicket = 0;
        private void TriggerFrame(Object state)
        {
            while (true)
            {
                // 10_000_000 = 1s
                var current = DateTime.Now.Ticks;
                if (current - lastTicket > 20_0000)
                {
                    lastTicket = current;
                    nes.Frame();
                }
            }
            //timer.Change(TimeSpan.FromMilliseconds(5), Timeout.InfiniteTimeSpan);
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if(nes == null)
            {
                return;
            }
            if (e.KeyData == Keys.W)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.UP, true);
            }
            else if (e.KeyData == Keys.A)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.LEFT, true);
            }
            else if (e.KeyData == Keys.S)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.DOWN, true);
            }
            else if (e.KeyData == Keys.D)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.RIGHT, true);
            }
            else if (e.KeyData == Keys.G)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.SELECT, true);
            }
            else if (e.KeyData == Keys.H)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.START, true);
            }
            else if (e.KeyData == Keys.K)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.A, true);
            }
            else if (e.KeyData == Keys.L)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.B, true);
            }
        }

        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            if (nes == null)
            {
                return;
            }
            if (e.KeyData == Keys.W)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.UP, false);
            }
            else if (e.KeyData == Keys.A)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.LEFT, false);
            }
            else if (e.KeyData == Keys.S)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.DOWN, false);
            }
            else if (e.KeyData == Keys.D)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.RIGHT, false);
            }
            else if (e.KeyData == Keys.G)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.SELECT, false);
            }
            else if (e.KeyData == Keys.H)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.START, false);
            }
            else if (e.KeyData == Keys.K)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.A, false);
            }
            else if (e.KeyData == Keys.L)
            {
                nes.Ctrl1.SetBtnStatus(CtrlBtn.B, false);
            }
        }

        private void BtnLoadNes_Click(object sender, EventArgs e)
        {
            var result = openFileDialog1.ShowDialog(this);
            if (result == DialogResult.OK)
            {
                var nesPath = openFileDialog1.FileName;
                this.txtBoxNesPath.Text = nesPath;

                Release();

                try
                {
                    audioWaveProvider = new AudioWaveProvider
                    {
                        ReadCallBack = null
                    };
                    timer = new System.Threading.Timer(TriggerFrame, null, Timeout.Infinite, Timeout.Infinite);
                    timer2 = new System.Threading.Timer(RefreshStatus, null, Timeout.Infinite, Timeout.Infinite);
                    player = new WaveOut();
                    player.Init(audioWaveProvider);
                    var nesByte = Utils.ReadFile(nesPath);
                    //var data = Utils.ReadFile2(nesPath);
                    nes = new Nes.Core.Nes(nesByte, new NesOption
                    {
                        OnFrame = HandleFrame,
                        SampleRate = 44100,
                        OnAudioSample = audioWaveProvider.Write,
                    });
                    timer.Change(TimeSpan.FromSeconds(2), Timeout.InfiniteTimeSpan);
                    timer2.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
                    player.Play();
                }
                catch
                {
                    
                }
            }
        }


        private void Release()
        {
            audioWaveProvider?.Dispose();
            audioWaveProvider=null;
            player?.Dispose();
            player = null;
        }
    }
}
